<template>
  <div class="panorama">
    <div ref="threeBox" class="threeBox"></div>
    <!-- 提示内容 -->
    <div class="tooltip-box" :style="tooltipPosition" ref="tooltipBox">
      <div class="container">
        <div class="title">标题：{{ tooltopContent.title }}</div>
        <div class="explain">说明：{{ tooltopContent.text }}</div>
      </div>
    </div>
    <p class="title-text" ref="titleBox" :style="titlePosition">
      {{ tooltopContent.title }}
    </p>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
export default {
  name: "Panorama",
  data() {
    return {
      scene: null,
      camera: null,
      renderer: null,
      time: null,
      controls: null,
      sphere: null, // 显示的球体
      picList: ["left", "right", "top", "bottom", "front", "back"],
      dataList: [
        {
          image: require("@/assets/livingRoom.jpg"),
          tipsList: [
            {
              position: { x: -200, y: -4, z: -147 },
              content: {
                title: "进入厨房",
                text: "",
                image: 1,
                showTip: false,
                showTitle: true,
              },
            },
            {
              position: { x: -100, y: 0, z: -231 },
              content: {
                title: "信息点2",
                text: "77989",
                showTip: true,
                showTitle: false,
              },
            },
            {
              position: { x: 150, y: -50, z: -198 },
              content: {
                title: "信息点3",
                text: "qwdcz",
                showTip: true,
                showTitle: false,
              },
            },
            {
              position: { x: 210, y: 11, z: -140 },
              content: {
                title: "信息点4",
                text: "大豆食心虫侦察十大大苏打大大大大大大大",
                showTip: true,
                showTitle: false,
              },
            },
            {
              position: { x: 208, y: -12, z: 140 },
              content: {
                title: "信息点5",
                text: "eq",
                showTip: true,
                showTitle: false,
              },
            },
            {
              position: { x: 86, y: -9, z: 236 },
              content: {
                title: "进入房间",
                text: "",
                showTip: false,
                showTitle: true,
              },
            },
          ],
        },
        {
          image: require("@/assets/kitchen.jpg"),
          tipsList: [
            {
              position: { x: -199, y: -24, z: 145 },
              content: {
                title: "进入大厅",
                text: "",
                image: 0,
                showTip: false,
                showTitle: true,
              },
            },
          ],
        },
      ],
      tipsSpriteList: [],
      tooltipPosition: {
        top: "-100%",
        left: "-100%",
      },
      titlePosition: {
        top: "-100%",
        left: "-100%",
      },
      tooltopContent: {},
      LEFTSIDE: 220, // 因为不在0,0的位置上，要减去那一部分
      TOPSIDE: 80
    };
  },
  methods: {
    // 创建场景
    initScene() {
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color("#cccccc");
    },
    // 创建摄像机
    initCamera(element) {
      this.camera = new THREE.PerspectiveCamera(
        45,
        element.clientWidth / element.clientHeight,
        0.1,
        2000
      );
      this.camera.position.set(50, 0, 40);
    },
    // 创建渲染器
    initRenderer(element) {
      this.renderer = new THREE.WebGLRenderer();
      // 设置渲染器的初始颜色
      // this.renderer.setClearColor(new THREE.Color("skyblue"));
      // 设置输出canvas画面的大小
      this.renderer.setSize(element.offsetWidth, element.offsetHeight);
      element.appendChild(this.renderer.domElement);
    },
    // 初始化内容
    initContent(index = 0) {
      const sphereGeometry = new THREE.SphereGeometry(16, 50, 50);
      sphereGeometry.scale(16, 16, -16);
      const texture = new THREE.TextureLoader().load(
        this.dataList[index].image
      );
      const sphereMaterial = new THREE.MeshBasicMaterial({ map: texture });
      this.sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
      this.scene.add(this.sphere);
      this.addTips();
    },
    // 添加提示信息
    addTips(index = 0) {
      let tipTexture = new THREE.TextureLoader().load(
        require("@/assets/tip.png")
      );
      let material = new THREE.SpriteMaterial({ map: tipTexture });
      this.tipsSpriteList = [];
      this.dataList[index].tipsList.forEach((item) => {
        let sprite = new THREE.Sprite(material);
        sprite.scale.set(10, 10, 10);
        sprite.position.set(item.position.x, item.position.y, item.position.z);
        sprite.content = item.content;
        this.tipsSpriteList.push(sprite);
        this.scene.add(sprite);
      });
    },
    // 初始化控制器
    initControl(element) {
      this.controls = new OrbitControls(this.camera, element);
      // 能够将相机向内移动多少
      this.controls.minDistance = 1;
      // 能够将相机向外移动多少
      this.controls.maxDistance = 100;
      // 启用或禁用摄像机平移
      this.controls.enablePan = true;
    },
    render() {
      // this.controls.update();
      this.renderer.render(this.scene, this.camera);
      this.timer = requestAnimationFrame(this.render);
    },

    // 鼠标移动
    onMouseMove(e) {
      e.preventDefault();
      let ele = this.$refs.threeBox;
      const raycaster = new THREE.Raycaster();
      const pointer = new THREE.Vector2();
      pointer.x = ((e.clientX - this.LEFTSIDE) / ele.clientWidth) * 2 - 1;
      pointer.y = -((e.clientY - this.TOPSIDE) / ele.clientHeight) * 2 + 1;
      //通过鼠标点的位置和当前相机的矩阵计算出raycaster
      raycaster.setFromCamera(pointer, this.camera);

      // 返回射线选中的对象 第二个参数如果不填 默认是false
      const intersects = raycaster.intersectObjects(this.tipsSpriteList, true);
      // 判斷如果有射线交汇的点就有值
      if (intersects.length) {
        let eleWidth = ele.clientWidth / 2;
        let eleHeight = ele.clientHeight / 2;
        let worldVector = new THREE.Vector3(
          intersects[0].object.position.x,
          intersects[0].object.position.y,
          intersects[0].object.position.z
        );
        let position = worldVector.project(this.camera);
        this.tooltopContent = intersects[0].object.content;
        // 如果有提示的就弹出提示信息
        if (intersects[0].object.content.showTip) {
          let left = Math.round(
            eleWidth * position.x +
              eleWidth -
              this.$refs.tooltipBox.clientWidth / 2
          );
          let top = Math.round(
            -eleHeight * position.y +
              eleHeight -
              this.$refs.tooltipBox.clientHeight / 2
          );
          this.tooltipPosition = {
            left: `${left}px`,
            top: `${top}px`,
          };
        } else if (intersects[0].object.content.showTitle) {
          // 如果要显示标题
          let left = Math.round(
            eleWidth * position.x +
              eleWidth -
              this.$refs.titleBox.clientWidth / 2
          );
          let top = Math.round(-eleHeight * position.y + eleHeight);
          this.titlePosition = {
            left: `${left}px`,
            top: `${top}px`,
          };
        }
      } else {
        // 隐藏提示信息
        this.handleTooltipHide(e);
      }
    },

    // 鼠标点击
    onClick(e){
      e.preventDefault();
      let ele = this.$refs.threeBox;
      const raycaster = new THREE.Raycaster();
      const pointer = new THREE.Vector2();
      pointer.x = ((e.clientX - this.LEFTSIDE) / ele.clientWidth) * 2 - 1;
      pointer.y = -((e.clientY - this.TOPSIDE) / ele.clientHeight) * 2 + 1;
      //通过鼠标点的位置和当前相机的矩阵计算出raycaster
      raycaster.setFromCamera(pointer, this.camera);
      // 计算物体与射线的焦点
      const intersects = raycaster.intersectObjects(this.tipsSpriteList)
      console.log('intersects', intersects)
      if(intersects.length && intersects[0].object.content.showTitle){
        // 转换场景重新添加提示信息
        this.changeSceneAndAddTip(intersects[0].object.content.image)
        // 隐藏当前场景的提示信息
        this.handleTooltipHide(e)
      }
    },

    // 窗口缩放
    onResize(){
      let ele = this.$refs.threeBox
      const eleWidth = ele.clientWidth
      const eleHeight = ele.clientHeight
      // 更新摄像机投影矩阵
      this.camera.updateProjectionMatrix()
      this.renderer.setSize(eleWidth, eleHeight)
    },

    // 转换场景和添加提示
    changeSceneAndAddTip(index=0){
      // 移除之前添加在场景上的图标
      this.scene.children = this.scene.children.filter(
        (item) => String(item.type) !== "Sprite"
      );
      const texture = new THREE.TextureLoader().load(this.dataList[index].image)
      const sphereMaterial = new THREE.MeshBasicMaterial({map: texture})
      // 改变球体显示的图片
      this.sphere.material = sphereMaterial
      // 清空已有的提示
      this.tipsSpriteList = []
      this.camera.updateProjectionMatrix();
      this.addTips(index)
    },
    handleTooltipHide(e) {
      e.preventDefault();
      (this.tooltipPosition = {
        top: "-100%",
        left: "-100%",
      }),
        (this.titlePosition = {
          top: "-100%",
          left: "-100%",
        }),
        (this.tooltopContent = {});
    },
  },
  mounted() {
    let element = this.$refs.threeBox;
    this.initScene();
    this.initCamera(element);
    this.initControl(element);
    this.initContent();
    this.initRenderer(element);
    this.render();
    // 监听鼠标移入画布的范围
    this.renderer.domElement.addEventListener(
      "mousemove",
      this.onMouseMove,
      false
    );
    // 监听鼠标在画布上点击的事件
    window.addEventListener("click", this.onClick, false);
    // 监听窗口的缩放时间
    window.addEventListener("resize", this.onResize, false)
  },
};
</script>
<style lang="less" scoped>
.threeBox {
  width: 100%;
  height: 100%;
}
.panorama {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  .view-container {
    width: 100%;
    height: 100%;
    overflow: hidden;
  }
  .title-text {
    position: absolute;
    width: 66px;
    color: #382129;
    cursor: pointer;
  }
  .tooltip-box {
    position: absolute;
    padding: 0px 0px 40px 0px;
    line-height: 30px;
    border-radius: 4px;
    color: #ffffff;
    z-index: 100;
    cursor: pointer;
    .container {
      position: relative;
      width: 240px;
      max-height: 200px;
      padding: 10px;
      background-color: rgba(0, 0, 0, 0.6);
      // &::before {
      //   content: "";
      //   position: absolute;
      //   bottom: -16px;
      //   left: 50%;
      //   border-top: 16px solid rgba(0, 0, 0, 0.8);
      //   border-left: 10px solid transparent;
      //   border-right: 10px solid transparent;
      // }
      .title {
        width: 100%;
        padding: 6px 0;
      }
      .explain {
        width: 100%;
        max-height: 100px;
        font-size: 14px;
        overflow-y: auto;
        &::-webkit-scrollbar {
          width: 4px;
          height: 4px;
        }
        &::-webkit-scrollbar-track {
          background: #353535;
          border-radius: 2px;
        }
        &::-webkit-scrollbar-thumb {
          background: #cdcdcd;
          border-radius: 2px;
        }
        &::-webkit-scrollbar-thumb:hover {
          background: #9c9c9c;
        }
        &::-webkit-scrollbar-corner {
          background: #f6f6f6;
        }
      }
    }
  }
}
</style>
